#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <functional>
#include "Task.hpp"

// typedef std::is_function<void()> work_t;
using work_t = std::function<void()>;

enum
{
    OK = 0,
    UseageError,
    PipeError,
    ForkError
};

class Channel
{
public:
    Channel(int wfd, pid_t who)
        : _wfd(wfd)
    {
        _name = "Channel " + std::to_string(wfd) + "-" + std::to_string(who);
    }

    std::string Name()
    {
        return _name;
    }

    void Send(int cmd_num)
    {
        ::write(_wfd,&cmd_num,sizeof(cmd_num));
    }

    ~Channel()
    {
    }

private:
    int _wfd; // 管道写端的文件描述符
    std::string _name;
    pid_t _who; // 子进程的pid
};

void Useage(std::string p)
{
    std::cout << "运行进程方式：" << p << " process_name + number" << std::endl;
}

void worker()
{
}

void debug(std::vector<Channel> &channels)
{
    for (auto &e : channels)
    {
        std::cout << e.Name() << std::endl;
    }
}

void InitPool(int num, std::vector<Channel> &channels, work_t work)
{
    // 循环创建子进程
    int i = 0;
    for (i = 0; i < num; ++i)
    {
        // 1、创建管道
        // 由于要实现父子进程看到同一份资源，子进程创建是会拷贝父进程的内核数据结构，因此要先创建管道，然后创建子进程
        int pipefd[2] = {0}; // 获取管道的读写端
        int n = pipe(pipefd);
        // 该函数参数是输出型参数，成功返回0
        if (n < 0)
        {
            perror("pipe error");
            exit(PipeError);
        }

        // 2、创建子进程:
        pid_t id = fork();
        if (id < 0)
        {
            perror("fork error");
            exit(ForkError);
        }
        else if (id == 0)
        {
            // child：子进程在执行后直接退出，因为循环创建子进程只需要父进程执行
            // 子进程：读
            close(pipefd[1]);
            dup2(pipefd[0], 0); // 管道读端直接从标准输入读取
            work();
            exit(0);
        }

        // 父进程：写
        close(pipefd[0]);
        // 每次循环后，走到此处就说明有一个管道创建好了，此时就把写端的描述符加入到数组，进行管理
        channels.emplace_back(pipefd[1], id); // 传入的是写端文件描述符和与之相连的子进程
    }
}

int main(int argc, char *argv[])
{
    // 此处提示用户该如何运行该进程
    if (argc != 2)
    {
        Useage(argv[0]);
        exit(1);
    }
    // 此时argv[1]中就是创建子进程的个数，只不过是char*形式，
    int num_proc = std::stoi(argv[1]);
    std::vector<Channel> channels;

    // 1、初始化进程池
    InitPool(num_proc, channels, worker);
    // debug(channels);

    // 2、分配任务
    int who=0;
    int N=20;
    while (N--)
    {
        // （1）选择任务码
        int num = tm.SelectTask();
        //（2）选择一个进程
        Channel cur=channels[who++];
        who %= channels.size();
        //（3）分发任务
        std::cout<<"********************************"<<std::endl;
        std::cout<<"send "<<num<<" to"<<cur.Name()<<",还剩下："<<N<<std::endl;
        std::cout<<"********************************"<<std::endl;
        cur.Send(num);
        sleep(1);
    }
    return 0;
}